package com.imooc.course.file.controller.admin;

import com.alibaba.fastjson.JSON;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.AppendObjectRequest;
import com.aliyun.oss.model.AppendObjectResult;
import com.aliyun.oss.model.ObjectMetadata;
import com.imooc.course.file.form.FileForm;
import com.imooc.course.server.dto.FileDto;
import com.imooc.course.server.dto.ResponseDto;
import com.imooc.course.server.enums.FileUseEnum;
import com.imooc.course.server.service.FileService;
import com.imooc.course.server.utils.CopyUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.*;

@RestController
@RequestMapping("/oss/admin")
public class OssController {

    private static final Logger log = LoggerFactory.getLogger(OssController.class);
    public static final String BUSINESS_NAME = "OSS文件上传";

    @Value("${oss.bucket}")
    private String bucket;

    @Value("${oss.endpoint}")
    private String endpoint;// Endpoint以杭州为例，其它Region请按实际情况填写。

    // 阿里云主账号AccessKey拥有所有API的访问权限，风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维，请登录RAM控制台创建RAM用户。
    @Value("${oss.accessKeyId}")
    private String accessKeyId;//子RAM用户accessKeyId

    @Value("${oss.accessKeySecret}")
    private String accessKeySecret;//子RAM用户accessKeySecret

    @Value("${oss.basePath}")
    private String basePath;

    @Value("${file.ossPath}")
    private String FILE_OSS_PATH;

    @Value("${file.path}")
    private String FILE_PATH;

    @Resource(name = "fileService")
    private FileService fileService;

    @PostMapping("/upload")
    public ResponseDto ossUpload(@RequestParam MultipartFile shard, String param) throws IOException, InterruptedException {
        //将接收到的文件转换为流
        InputStream in = shard.getInputStream();

        //将字符串参数还原为对象并拼接文件访问路径
        FileForm fileForm = JSON.parseObject(param, FileForm.class);
        FileUseEnum useEnum = FileUseEnum.getByCode(fileForm.getUse());
        String dir = useEnum.name().toLowerCase();

        String path = new StringBuffer(basePath)//存储到阿里云OSS时使用的path
                .append(dir)
                .append("/")
                .append(fileForm.getName())
                .toString();

        //失败3次放弃上传。这里用id传值显得不规范。
        if ("3".equals(fileForm.getId())) {
            this.removeFile(fileForm,path);
            FileDto dto = new FileDto();
            dto.setPath("");
            return ResponseDto.fail().setContent(dto);
        }

        Integer code = this.oss(in, fileForm, path);

        FileDto fileDto = CopyUtil.copy(fileForm, FileDto.class);

        if (code == 0) {
            log.info("开始保存文件记录");
            fileDto.setPath(path);
            fileService.save(fileDto);
            fileDto.setPath(FILE_OSS_PATH+path);
            return ResponseDto.success().setCode(String.valueOf(code)).setContent(fileDto);
        } else {
            fileDto.setShardIndex(code);
            fileDto.setPath("");
            return ResponseDto.success().setCode(String.valueOf(code)).setContent(fileDto);
        }


    }

    private int oss(InputStream in, FileForm fileForm, String path) {
        int temp = 0;
        try {
            // 创建OSSClient实例。
            OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

            ObjectMetadata meta = new ObjectMetadata();
            // 指定上传的内容类型。
            meta.setContentType("text/plain");

            // 通过AppendObjectRequest设置多个参数。
            AppendObjectRequest appendObjectRequest = new AppendObjectRequest(bucket, path, in, meta);
            appendObjectRequest.setMetadata(meta);

            /* 示例：
        // 通过AppendObjectRequest设置单个参数。
        // 设置Bucket名称。
        //appendObjectRequest.setBucketName("<yourBucketName>");
        // 设置Object名称。即不包含Bucket名称在内的Object的完整路径，例如example/test.txt。
        //appendObjectRequest.setKey("<yourObjectName>");
        // 设置待追加的内容。有两种可选类型：InputStream类型和File类型。这里为InputStream类型。
        //appendObjectRequest.setInputStream(new ByteArrayInputStream(content1.getBytes()));
        // 设置待追加的内容。有两种可选类型：InputStream类型和File类型。这里为File类型。
        //appendObjectRequest.setFile(new File("<yourLocalFile>"));
        // 指定文件的元信息，第一次追加时有效。
        //appendObjectRequest.setMetadata(meta);
        */

            // 第一次追加。
            // 设置文件的追加位置。
            int idx = (fileForm.getShardIndex() - 1) * fileForm.getShardSize();
            appendObjectRequest.setPosition(Long.valueOf(idx));

            //关键代码，前面的都是准备，这里才真正将文件上传到阿里云OSS储存中。
            AppendObjectResult appendObjectResult = ossClient.appendObject(appendObjectRequest);


            // 文件的64位CRC值。此值根据ECMA-182标准计算得出。
            //System.out.println(appendObjectResult.getObjectCRC());

            /* 示例：
        // 第二次追加。
        // nextPosition指明下一次请求中应当提供的Position，即文件当前的长度。
        appendObjectRequest.setPosition(appendObjectResult.getNextPosition());
        appendObjectRequest.setInputStream(new ByteArrayInputStream(content2.getBytes()));
        appendObjectResult = ossClient.appendObject(appendObjectRequest);

        // 第三次追加。
        appendObjectRequest.setPosition(appendObjectResult.getNextPosition());
        appendObjectRequest.setInputStream(new ByteArrayInputStream(content3.getBytes()));
        appendObjectResult = ossClient.appendObject(appendObjectRequest);
        */

            // 关闭inputStream。
            in.close();
            // 关闭OSSClient。
            ossClient.shutdown();
        } catch (Exception e) {
            temp = fileForm.getShardIndex();
            e.printStackTrace();
        }
        return temp;
    }

    private void removeFile(FileForm fileForm, String path ) {
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        ossClient.deleteObject(bucket, path);
        ossClient.shutdown();
        fileService.removeByKey(fileForm.getKey());
    }

    @GetMapping("/check/{key}")
    public ResponseDto checkShardIndex(@PathVariable("key") String key) {
        FileDto fileDto = fileService.getByShardIndex(key);
        if (fileDto != null) {
            fileDto.setPath(FILE_OSS_PATH + fileDto.getPath());
        }
        return ResponseDto.success().setContent(fileDto);
    }


    private File convertForFile(MultipartFile mf) throws IOException {

        File file = new File(mf.getOriginalFilename());

        InputStream in = mf.getInputStream();
        OutputStream out = new FileOutputStream(file);

        byte[] b = new byte[10 * 1024 * 1024];
        int len = 0;
        while ((len = in.read(b)) != -1) {
            out.write(b,0,len);
        }

        in.close();
        out.close();
        return file;
    }


}
